#include "bp/bp_error.h"

static LONG volatile _bp_error_init_state = 0;
static DWORD _bp_tls_index = TLS_OUT_OF_INDEXES;

static void _bp_error_init();
static bp_error_t * _bp_error_buf_get();

_BP_EXPORT bp_error_t _BP_API bp_get_lasterror()
{
	bp_uint32_t *ptr = _bp_error_buf_get();
	if (!ptr)
	{
		return BP_ERROR_BUF_ALLOC_FAILED;
	}

	return *ptr;
}

_BP_EXPORT void _BP_API bp_set_lasterror(bp_error_t err)
{
	bp_uint32_t *ptr = _bp_error_buf_get();
	if (!ptr)
	{
		return;
	}

	*ptr = err;
}

void _bp_error_init()
{
	LONG state;
	do 
	{
		state = InterlockedCompareExchange(&_bp_error_init_state, 1, 0);
		if (state == 1)
		{
			Sleep(1);
		}
	} while (state == 1);

	if (state == 0 && InterlockedCompareExchange(&_bp_error_init_state, 1, 1) == 1)
	{
		_bp_tls_index = TlsAlloc();
		InterlockedCompareExchange(&_bp_error_init_state, 2, 1);
	}
}

bp_error_t * _bp_error_buf_get()
{
	bp_uint32_t *ptr = NULL;

	_bp_error_init();
	if (_bp_tls_index == TLS_OUT_OF_INDEXES)
	{
		return NULL;
	}

	ptr = (bp_uint32_t *)TlsGetValue(_bp_tls_index);
	if (!ptr)
	{
		if (GetLastError() != ERROR_SUCCESS)
		{
			return NULL;
		}

		ptr = (bp_uint32_t *)LocalAlloc(LPTR, sizeof(*ptr));
		if (ptr)
		{
			*ptr = BP_ERROR_OK;
		}

		if (!ptr || !TlsSetValue(_bp_tls_index, ptr))
		{
			LocalFree(ptr);
			return NULL;
		}
	}

	return ptr;
}
